package main

import (
	"bytes"
	"fmt"
)
/*
	buffer.Len(): 需要记住的是，Buffer值的长度是未读内容的长度，而不是已存内容的总长度。

	buffer.Cap()：Buffer值的容量指的是它的内容容器（也就是那个字节切片）的容量，它只与在当前值之上的写操作有关，并会随着内容的写入而不断增长。

在已读计数代表的索引之前的那些内容，永远都是已经被读过的，它们几乎没有机会再次被读取。

bytes.Buffer类型的值记录的已读计数，在其中起到了怎样的作用？
	1.读取内容时，相应方法会依据已读计数找到未读部分，并在读取后更新计数。
	2.写入内容时，如需扩容，相应方法会根据已读计数实现扩容策略。
	3.截断内容时，相应方法截掉的是已读计数代表索引之后的未读部分。
	4.读回退时，相应方法需要用已读计数记录回退点。
	5.重置内容时，相应方法会把已读计数置为0。
	6.导出内容时，相应方法只会导出已读计数代表的索引之后的未读部分。
	7.获取长度时，相应方法会依据已读计数和内容容器的长度，计算未读部分的长度并返回。


问题 1：bytes.Buffer的扩容策略是怎样的？
	1.Buffer值既可以被手动扩容，也可以进行自动扩容。并且，这两种扩容方式的策略是基本一致的。所以，除非完全确定后续内容所需的字节数，否则让Buffer值自动去扩容就好了。

	2.在扩容的时候，Buffer值中相应的代码（以下简称扩容代码）会先判断内容容器的剩余容量，是否可以满足调用方的要求，或者是否足够容纳新的内容。

	3.如果可以，那么扩容代码会在当前的内容容器之上，进行长度扩充。
		更具体地说，如果内容容器的容量与其长度的差，大于或等于另需的字节数，那么扩容代码就会通过切片操作对原有的内容容器的长度进行扩充
		b.buf = b.buf[:length+need]

	4.反之，如果内容容器的剩余容量不够了，那么扩容代码可能就会用新的内容容器去替代原有的内容容器，从而实现扩容。

	a.一步优化:
  		如果当前内容容器的容量的一半，仍然大于或等于其现有长度再加上另需的字节数的和.
		cap(b.buf)/2 >= len(b.buf)+need
	扩容代码就会复用现有的内容容器，并把容器中的未读内容拷贝到它的头部位置。这也意味着其中的已读内容，将会全部被未读内容和之后的新内容覆盖掉。

	b.当前内容容器的容量小于新长度的二倍?
		扩容代码就只能再创建一个新的内容容器，并把原有容器中的未读内容拷贝进去，最后再用新的容器替换掉原有的容器。这个新容器的容量将会等于原有容量的二倍再加上另需字节数的和。

		新容器的容量 =2* 原有容量 + 所需字节数

	c.对于处在零值状态的Buffer值来说，如果第一次扩容时的另需字节数不大于64，那么该值就会基于一个预先定义好的、长度为64的字节数组来创建内容容器。
在这种情况下，这个内容容器的容量就是64。这样做的目的是为了让Buffer值在刚被真正使用的时候就可以快速地做好准备。


问题 2：bytes.Buffer中的哪些方法可能会造成内容的泄露？

	概念：
		内容泄露是指，使用Buffer值的一方通过某种非标准的（或者说不正式的）方式，得到了本不该得到的内容。

	bytes.Buffer中，Bytes方法和Next方法都可能会造成内容的泄露。原因在于，它们都把基于内容容器的切片直接返回给了方法的调用方。

	通过切片，可以直接访问和操纵它的底层数组。 详见 下面的例子 代码 81 ～ 108

*/

func main() {

	// 声明了一个bytes.Buffer类型的变量buffer
	var buffer bytes.Buffer
	contents := "Simple byte buffer for marshaling data."
	fmt.Printf("Writing contents %q ...\n", contents)

	// 写入了一个字符串
	buffer.WriteString(contents)
	fmt.Printf("The length of buffer: %d\n", buffer.Len()) // The length of buffer: 39
	fmt.Printf("The capacity of buffer: %d\n", buffer.Cap()) // The capacity of buffer: 64

	// 实际上，与strings.Reader类型的Len方法一样，buffer的Len方法返回的也是内容容器中未被读取部分的长度，而不是其中已存内容的总长度
	p1 := make([]byte, 7)
	// 从buffer中读取一部分内容，并用它们填满长度为7的字节切片p1之后，buffer的Len方法返回的结果值也会随即发生变化。
	n, _ := buffer.Read(p1)
	fmt.Printf("%d bytes were read. (call Read)\n", n)
	fmt.Printf("The length of buffer: %d\n", buffer.Len()) // 这个缓冲区的长度已经变为了32。
	fmt.Printf("The capacity of buffer: %d\n", buffer.Cap()) // 因为并没有再向该缓冲区中写入任何内容，所以它的容量会保持不变，仍是64。


	contents01 := "ab"
	// 用字符串值"ab"初始化了一个Buffer值
	buffer01 := bytes.NewBufferString(contents01)
	/*
		为什么容量是8?
		TODO
	 */
	fmt.Printf("The capacity of new buffer with contents %q: %d\n", contents01, buffer01.Cap()) // 内容容器的容量为：8。
	unreadBytes := buffer01.Bytes()
	fmt.Printf("The unread bytes of the buffer: %v\n", unreadBytes) //  未读内容为：[97 98]。

	// 向该值写入了字符串值"cdefg"
	buffer01.WriteString("cdefg")
	fmt.Printf("The capacity of buffer: %d\n", buffer01.Cap()) // 内容容器的容量仍为：8。
	/*
		前面通过调用buffer01的Bytes方法得到的结果值unreadBytes，包含了在那时其中的所有未读内容。

		由于这个结果值与buffer01的内容容器在此时还共用着同一个底层数组，所以，只需通过简单的再切片操作，就可以利用这个结果值拿到buffer1在此时的所有未读内容。
	 */
	unreadBytes = unreadBytes[:cap(unreadBytes)]
	// buffer01的新内容就被泄露出来
	fmt.Printf("The unread bytes of the buffer: %v\n", unreadBytes) // 基于前面获取到的结果值可得，未读内容为：[97 98 99 100 101 102 103 0]。


	// 如果把unreadBytes的值传到了外界，那么外界就可以通过该值操纵buffer1的内容了
    // 相当于外部修改内部的buffer值了
	unreadBytes[len(unreadBytes) -2] = byte('X')
	fmt.Printf("The unread bytes of the buffer: %v\n", buffer01.Bytes()) // 未读内容变为了：[97 98 99 100 101 102 88]。

}
